// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include <QtGui>
#include <QtQuick3D/qquick3d.h>
#include <QPropertyAnimation>
#include <QQmlContext>
#include <QElapsedTimer>
#include <QQuickWindow>
#include <QThreadPool>
#include <QRunnable>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>

//帧率是每秒60帧，单位是秒
const double  DELTA_FPS_TIME = 1.0/60.0;

class SaveImageTask : public QRunnable
{
    public:
        SaveImageTask(const QImage &image, const QString &fileName)
            : m_image(image), m_fileName(fileName) {}

        void run() override
        {

            bool saved = m_image.save(m_fileName);

            if (saved)
            {
                //qDebug() << "Image saved successfully:" << m_fileName;
            }
            else
            {
                qDebug() << "Failed to save image:" << m_fileName;
            }
        }

    private:
        QImage m_image;
        QString m_fileName;
};


class BallManager : public QObject
{
        Q_OBJECT
    public:
        explicit BallManager(QObject* parent = nullptr) : QObject(parent),
            m_damping(0.95) //阻尼系数
        {

            m_CamaraPosition = QVector3D(0, 0, 800);
            m_CamaraRotation = QVector3D(0, 0, 0);
            m_jsonvelocity = QVector3D(0, 0, 0);
            m_jsonposition = QVector3D(-500, 250, 0);


            QString exePath = QCoreApplication::applicationDirPath();

            // 使用当前日期和时间创建目录名
            QString dateTimeStr = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
            m_strImageDir = exePath + "/" + dateTimeStr;

            // 创建目录
            QDir dir;

            if (!dir.exists(m_strImageDir))
            {
                if (!dir.mkpath(m_strImageDir))
                {
                    qDebug() << "Failed to create directory:" << m_strImageDir;
                }
            }

            QString strJsonPath = exePath +"/config.json";

            if (!QFile::exists(strJsonPath))
            {
                // 如果不存在，从资源文件中复制一份出来
                QFile resourceFile(":config.json"); // 假设config.json在资源文件中

                if (resourceFile.open(QIODevice::ReadOnly))
                {
                    QFile newFile(strJsonPath);

                    if (newFile.open(QIODevice::WriteOnly))
                    {
                        newFile.write(resourceFile.readAll());
                        newFile.close();
                        resourceFile.close();
                        qDebug() << "config.json created from resources.";
                    }
                    else
                    {
                        qDebug() << "Could not create config.json.";
                    }
                }
                else
                {
                    qDebug() << "Resource file could not be opened.";
                }
            }
            else
            {
                qDebug() << "config.json already exists.";
            }

            QFile file(strJsonPath);

            if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            {
                qDebug() << "Failed to open the file.";
                return ;
            }

            // 解析 JSON 文件
            QByteArray jsonData = file.readAll();
            QJsonParseError error;
            QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);

            if (error.error != QJsonParseError::NoError)
            {
                qDebug() << "Error parsing JSON:" << error.errorString();
                return ;
            }

            // 检查是否为对象
            if (!jsonDoc.isObject())
            {
                qDebug() << "JSON is not an object.";
                return ;
            }

            QJsonObject jsonObject = jsonDoc.object();

            // 获取 摄像机 和 小球 对象
            QJsonObject cameraObj = jsonObject["camera"].toObject();
            QJsonObject ballObj   = jsonObject["ball"].toObject();

            // 提取值
            m_CamaraRotation.setX(cameraObj["rotation_x"].toDouble());
            m_CamaraRotation.setY(cameraObj["rotation_y"].toDouble());
            m_CamaraRotation.setZ(cameraObj["rotation_z"].toDouble());

            m_CamaraPosition.setX(cameraObj["position_x"].toDouble());
            m_CamaraPosition.setY(cameraObj["position_y"].toDouble());
            m_CamaraPosition.setZ(cameraObj["position_z"].toDouble());

            //更新X轴，Y轴方向的移动速度
            m_jsonvelocity.setX(ballObj["speed_x"].toDouble());
            m_jsonvelocity.setY(ballObj["speed_y"].toDouble());
            //更新小球的初始位置
            m_jsonposition.setX(ballObj["position_x"].toDouble());
            m_jsonposition.setY(ballObj["position_y"].toDouble());

            m_fGravity     = ballObj["gravity"].toDouble();
            m_fScaleFactor = ballObj["scale_factor"].toDouble();

            //更新小球的停止条件，0 弹跳速度小于22停止，用于小球弹跳分析
            //1 平抛，斜抛，自由落体运动
            m_nEndRules = jsonObject["endrules"].toInt();


            Reset();
        }

        Q_PROPERTY(QVector3D position READ position WRITE setPosition NOTIFY positionChanged)
        Q_PROPERTY(QVector3D camara_position CONSTANT READ camara_position )
        Q_PROPERTY(QVector3D camara_rotation CONSTANT READ camara_rotation )
        QVector3D position() const
        {
            return m_position;
        }
        QVector3D camara_position() const
        {
            return m_CamaraPosition;
        }
        QVector3D camara_rotation() const
        {
            return m_CamaraRotation;
        }
        void setPosition(const QVector3D &pos)
        {
            if (m_position != pos)
            {
                m_position = pos;
            }

            emit positionChanged(m_position);
        }

        void updatePosition()
        {
            //获取两次调用updatePosition函数的时间差 Δt, 这里的 Δt单位是毫秒
            qreal deltaTime = DELTA_FPS_TIME; //GetDeltaTime();
            //由于deltaTime 单位是ms，而重力加速度大约是 9.81 m/s²，由于绘图时，中心点向下的坐标
            //是Y轴的负方向，所以，规定重力的数值是 -9.81， 这样 G*Δt ，是Δt时刻后，速度增加的值，
            //单位是m/s，而我们绘图所用的单位是像素，因此需要把 m/s 转换成 像素/每秒， 像素的大小
            //决定着屏幕的大小，不同的显示分辨率的屏幕，这个数值并不是统一的，我们只能根据当前的
            //显示设备，粗略的估计720个像素对应的是20厘米的高度，所以，20cm换算成像素，需要乘以3600，
            //0.2x3600 = 720，而在这里Δt的单位是毫秒，因此我们需要换算成秒，所以还要除以1000；
            //3600 /1000 = 0.36, 这就是这个 ScaleFactor 系数的由来。

            //1155 个像素对应 124mm，1155/0.124 = 9314

            //像素速度 = 像素速度 + G*Δt*ScaleFactor
            m_velocity += m_gravity * deltaTime *m_fScaleFactor;

            //像素位移 = 像素速度*Δt
            m_position += m_velocity * deltaTime;

            //假设屏幕的底部是参考地面，检查是否有碰撞
            //无论哪种结束条件，超出X 屏幕范围，都重置
            if((m_position.x() >550) ||m_position.x()<-550)
            {
                Reset();

                if(m_nImageSaveFinish<2)
                {
                    m_nImageSaveFinish++;
                }
            }


            if((m_position.y() <= -300))
            {
                if(m_nEndRules == 0) //小球弹跳模式
                {
                    m_position.setY(-300); // Bounce back up
                    m_velocity.setY(-m_velocity.y() * m_damping); // 因为阻尼，弹跳后，y轴速度会阻尼减速

                    //Y轴方向的绝对值速度小于22像素/秒
                    if(abs(m_velocity.y())<22.0)
                    {
                        Reset();

                        //在程序运行的第二遍，才保存图片，因为渲染引擎第一次渲染较费时间，第二次渲染的时候，有
                        //缓存，速度较快。
                        if(m_nImageSaveFinish<2)
                        {
                            m_nImageSaveFinish++;
                        }
                    }
                }
                else //不弹跳
                {
                    Reset();

                    if(m_nImageSaveFinish<2)
                    {
                        m_nImageSaveFinish++;
                    }
                }
            }


            setPosition(m_position);

            if(m_nImageSaveFinish == 1)
            {
                //第二次循环才开始保存图片
                takeScreenshot();
            }

            //qDebug() << "Position Y:" << m_position.y()<<", Velocity Y:"<<m_velocity.y();

        }

        double GetDeltaTime()
        {
            static bool isFirstCall = true;

            double elapsedMilliseconds = 0;

            if (isFirstCall)
            {
                isFirstCall = false;
                m_etimer.start(); // 只在第一次调用时启动计时器
            }
            else
            {
                quint64 nanoseconds = m_etimer.nsecsElapsed(); //获取两次事件之间的纳秒数
                elapsedMilliseconds = static_cast<double>(nanoseconds) / 1000000.0;
                m_etimer.restart(); // 重新启动计时器，以便下一次调用时可以再次测量时间差
            }

            return elapsedMilliseconds;
        }

        void Reset()
        {
            m_position = m_jsonposition; //初始位置
            m_velocity = m_jsonvelocity;  //三轴速度
            m_gravity = QVector3D(0, -9.81f, 0); //重力加速度
            m_gravity.setY(m_fGravity);
            m_nImageNo = 0;

            //延时0.5秒再重新启动
            QThread::msleep(500);
            //GetDeltaTime();
        }

        void SetWindow(QQuickWindow* window)
        {
            m_window = window;
        }

        void takeScreenshot()
        {
            QImage image = m_window->grabWindow();
            saveImage(image);
        }

        void saveImage(const QImage &image)
        {
            // 保存截图到新创建的目录下
            QString fileName = m_strImageDir + "/Image" + QString::number(m_nImageNo++) + ".jpg";

            QThreadPool::globalInstance()->start(new SaveImageTask(image, fileName));

        }

    signals:
        void positionChanged(const QVector3D &position);

    private:

        QVector3D m_jsonposition; //从json 文件中读出的小球初始位置
        QVector3D m_jsonvelocity; //从json 文件中读出的小球的初始速度
        QVector3D m_velocity;
        QVector3D m_CamaraRotation;
        QVector3D m_CamaraPosition;
        QVector3D m_position;
        QVector3D m_gravity;
        qreal m_damping;
        QElapsedTimer m_etimer;
        QQuickWindow* m_window;
        int  m_nImageNo;
        int  m_nImageSaveFinish=0;
        QString m_strImageDir;
        int  m_nEndRules=0; //结束规则
        double m_fScaleFactor;
        double m_fGravity;

};


int main(int argc, char* argv[])
{
    QGuiApplication app(argc, argv);
    // 添加DLLs路径
    app.addLibraryPath(QDir::currentPath() + "/bin");
    // 添加插件路径
    QCoreApplication::addLibraryPath(QDir::currentPath() + "/plugins");

    // Register the BallManager type with the QML engine
    qmlRegisterType<BallManager>("BallManager", 1, 0, "BallManager");


    QSurfaceFormat::setDefaultFormat(QQuick3D::idealSurfaceFormat(4));

    QQmlApplicationEngine engine;
    // 添加QML文件路径
    engine.addImportPath(QDir::currentPath() + "/qml");

    BallManager ballManager;
    // Expose the BallManager instance to QML as 'ballManager'
    engine.rootContext()->setContextProperty("ballManager", &ballManager);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
    {
        return -1;
    }

    QQuickWindow* window = qobject_cast<QQuickWindow*>(engine.rootObjects().first());

    if (!window)
    {
        qFatal("Cannot access the root QQuickWindow.");
    }

    ballManager.SetWindow(window);

    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, &ballManager, &BallManager::updatePosition);
    timer.start(16); // 60 FPS


    return app.exec();
}

#include "main.moc"
